package com.whh.parseData.parseXml;

import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.whh.parseData.parseXml.anno.entry.ElementDesc;
import com.whh.parseData.parseXml.anno.helper.AnnoHelper;
import com.whh.parseData.parseXml.appoint.XmlFile;
import com.whh.parseData.parseXml.appoint.XmlFileDefaultImpl;
import com.whh.parseData.parseXml.utils.ClassUtil;

import java.io.File;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class ParseXml {

    public static XmlFile xmlFile = new XmlFileDefaultImpl();

    public static void init() {
        AnnoHelper.initBaseInfo();
    }

    public static <T> T parse(File file,Class<T> tClass) throws Exception{
        int index = file.getName().indexOf(".xml");
        if (index < 0) {
            System.out.println("此文件不是xml");
            return null;
        }
        String xmlFileType = xmlFile.getXmlFileType(file);
        T t  = tClass.newInstance();
        doParese(new SAXReader().read(file).getRootElement(), xmlFileType, t, 0);
        return t;
    }

    /**
     * 解析xml
     * @param element  元素的节点
     * @param object   该元素对应的对象
     * @param layer    元素的层级
     */
    private static void doParese(Element element, String xmlFileType, Object object, int layer) throws IllegalAccessException, InstantiationException, NoSuchFieldException, InvocationTargetException {
        ElementDesc elementDesc = AnnoHelper.getElementDesc(xmlFileType, element.getName(), layer);
        if (elementDesc == null || object == null || !elementDesc.isHasChild()) {
            return;
        }
        // 处理元素的属性
        List<Attribute> attributes = element.attributes();
        if (!attributes.isEmpty()) {
            for (Attribute attribute : attributes) {
                setValueForAttribute(elementDesc,attribute.getName(),object,attribute.getValue());
            }
        }
        // 处理元素的子元素
        Iterator iterator = element.elementIterator();
        int childLayer = ++layer; // 子元素层级
        while(iterator.hasNext()) {
            Element childElement = (Element) iterator.next();
            ElementDesc childElementDesc = AnnoHelper.getElementDesc(xmlFileType, childElement.getName(), childLayer);
            if (childElementDesc == null) {
                System.out.println("error 元素不存在" + childElement.getName()+layer);
            }

            String name = childElementDesc.getName();
            // 没有子节点的元素处理： 将其值赋值给父元素的属性
            if (elementDesc.getNoHashChildNames() != null && elementDesc.getNoHashChildNames().contains(name)) {
                setValueForElement(childElementDesc, name, object, childElement.getStringValue());
                continue;
            }

            // 有子节点元素处理
            // 创建子元素对应的对象
            Object childObject = childElementDesc.getClazz().newInstance();
            // 对于有子元素的节点，直接给父元素list属性赋值
            Field fieldList = ClassUtil.getFiled(elementDesc.getClazz(), "list");
            if (fieldList.get(object) == null) {
                fieldList.set(object, new ArrayList<>());
            }
            ((List)fieldList.get(object)).add(childObject);

            doParese(childElement, xmlFileType, childObject, childLayer);
        }
    }

    /**
     * 字段赋值优先级高于方法赋值
     * eg <root id=123456>  Class Root
     * @param elementDesc   元素   root
     * @param name          元素中属性名称  id
     * @param object        元素对应的对象  root对象
     * @param value         元素的值    123456
     * @throws IllegalAccessException
     */
    private static void setValueForAttribute(ElementDesc elementDesc, String name, Object object, String value) throws
            IllegalAccessException, InvocationTargetException {
        Field attributeFiled = elementDesc.getAttributeFiled(name);
        if (attributeFiled != null) {
            attributeFiled.set(object, value);
            return;
        }

        Method method = elementDesc.getAttributeMethod(name);
        if (method != null) {
            method.invoke(object, value);
        }
    }

    /**
     * 字段赋值优先级高于方法赋值
     * eg:<root><国家>中国<国家/> <root/>  root --> Root
     * @param childElement 父元素
     * @param childElementName    子元素名称
     * @param object   父元素对象 object
     * @param childElementValue     子元素value
     */
    private static void setValueForElement(ElementDesc childElement, String childElementName, Object object, String childElementValue)
            throws IllegalAccessException, InvocationTargetException {
        Field field = childElement.getField();
        if (field != null) {
            field.set(object, childElementValue);
            return;
        }

        Method method = childElement.getMethod();
        if (method != null) {
            method.invoke(object, childElementValue);
        }
    }

//    public static void main(String[] args) {
//        AnnoHelper.initBaseInfo();
//        File file = new File("D:\\code\\springboot-mybatis\\src\\main\\resources\\xml\\aaa_country.xml");
//        System.out.println(parse(file, Root.class));
//        File file1 = new File("D:\\code\\springboot-mybatis\\src\\main\\resources\\xml\\bbb_books.xml");
//        System.out.println(parse(file1, BookRoot.class));
//    }
}
